#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/5/5 13:58
# @Author : 闫峰
# @File    : handle_setup.py
import ast
import re
from typing import List
import jsonpath
from conf.settings import ensure_path_sep
from utils import GetYamlData
from utils.cache_process.cache_control import CacheHandler
from utils.dbUtils.db_pool import Database
from utils.dbUtils.mongodb_utils import MongoDBLink
from utils.dbUtils.redis_utils import RedisClient
from utils.dbUtils.sql_server_utils import MSSQL
from utils.logging_tool.log_control import ERROR, INFO
from utils.other_tools.models import load_module_functions
from utils.other_tools.regular_control import build_param
from utils.assertion import assert_type


class BaseTool:

    def ast(self, data):
        if isinstance(data, str):
            if data == 'null':
                return None
            else:
                try:
                    return ast.literal_eval(data)
                    # return eval(data)
                except Exception as e:
                    print(e)
                    return data
        else:
            return data

    def save_data(self, source: any, saves: List):
        for save in saves:
            save = save.strip()
            if save:
                key = save.split("=")[0]
                jsp = save.split("=")[1]
                if jsp.startswith("$"):
                    value = jsonpath.jsonpath(source.json(), jsp)
                    if value:
                        CacheHandler.update_cache(cache_name=key, value=value[0])
                        INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, value[0]))
                    else:

                        ERROR.logger.error("保存失败，参数为空")
                else:

                    value = re.search(jsp, source.text).group(1)

                    if value:
                        CacheHandler.update_cache(cache_name=key, value=value)
                        INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, value))
                    else:
                        ERROR.logger.error("保存失败，参数为空")

    def execute_setup_sql(self, db_name: str, setup_sql):
        """
        :param db_name: mysql:shopeev2.yaml:test_jdw_database   通过冒号将数据库实例类型  conf下配置文件名称 配置文件中的第一层key
                        拼接起来，完成多数据源的直接读取  目前数据源暂且支持mysql sqlserver2种
        :param setup_sql:
        :return:
        """
        # 解析数据库类型
        # mysql:walmart.yaml:test_jdw_database:test_zhcxkj_center_product
        db_type = db_name.split(":", maxsplit=1)[0]
        # 解析配置文件路径以及配置文件数据库key
        db_path_name = db_name.split(":", maxsplit=1)[1]
        # 如果数据库类型是mysql
        if db_type == "mysql":
            db_connect = Database(db_path_name)

            if setup_sql[0:6].upper() == "SELECT":
                result = db_connect.fetch_all(setup_sql, ())
                INFO.logger.info("执行前置sql====>{}，获得以下结果集:{}".format(setup_sql, result))
                # 获取所有查询字段，并保存至公共参数池
                self.handler_mysql_result(result)
            elif setup_sql[0:6].upper() != "SELECT":
                rows = db_connect.execute(setup_sql, ())


        elif db_type == "redis":
            r = RedisClient(db_path_name)
            func_name = setup_sql.split(":", maxsplit=1)[0]
            key = setup_sql.split(":", maxsplit=1)[1]
            func = getattr(r, func_name)
            func(key)
        elif db_type == "sqlserver":
            # sqlserver类型封装 后续使用在来封装
            db_connect = MSSQL(db_path_name)
            if setup_sql.lower().startswith("select"):
                # result_list = db_connect.ExecQuery_dict(setup_sql, ())
                # zq切换数据源包为pyodbc后修改查询转字典
                result_list = db_connect.ExecQuery_dict(setup_sql)
                INFO.logger.info("执行前置sql====>{}，获得以下结果集:{}".format(setup_sql, result_list))
                # # 获取所有查询字段，并保存至公共参数池
                # 查询到多条数据
                for i in range(len(result_list)):
                    for key in result_list[i].keys():
                        if i != 0:
                            new_key = key + str(i)
                        else:
                            new_key = key
                        CacheHandler.update_cache(cache_name=new_key, value=result_list[i][key])
                        INFO.logger.info("保存 {}=>{} 到全局变量池".format(new_key, result_list[i][key]))
            else:
                db_connect.ExecNonQuery(setup_sql)
        # for sql in setup_sql:
        #     result = db_connect.fetch_all(sql, ())
        #     if sql.lower().startswith("select"):
        #         INFO.logger.info("执行前置sql====>{}，获得以下结果集:{}".format(sql, result))
        #         # 获取所有查询字段，并保存至公共参数池
        #         for key in result.keys():
        #             CacheHandler.update_cache(cache_name=key, value=result[key])
        #             INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, result[key]))
        elif db_type == "mongodb":
            yaml_path, test_mongodb, dbname, coll_name = db_path_name.split(":")
            yaml_path = ensure_path_sep(f"\\conf\\{yaml_path}")
            uri = GetYamlData(yaml_path).get_yaml_data()[test_mongodb]
            mongodb = MongoDBLink(uri, dbname, coll_name)
            operating_mode, param = setup_sql.split(":", maxsplit=1)
            if operating_mode == "find":
                filter_sql, projection, sort = list(map(lambda x: ast.literal_eval(x), param.split("#")))
                result_list = mongodb.filter_projection_sort_limit(filter_sql, projection, sort)
                for result in result_list:
                    for key in result.keys():
                        CacheHandler.update_cache(cache_name=key, value=result[key])
                        INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, result[key]))
            elif operating_mode == "insert":
                pass

    def execute_sql(self, db_name: str, setup_sql):
        """
        :param db_name: mysql:shopeev2.yaml:test_jdw_database   通过冒号将数据库实例类型  conf下配置文件名称 配置文件中的第一层key
                        拼接起来，完成多数据源的直接读取  目前数据源暂且支持mysql sqlserver2种
        :param setup_sql:
        :return:
        """
        # 解析数据库类型
        # mysql:walmart.yaml:test_jdw_database:test_zhcxkj_center_product
        db_type = db_name.split(":", maxsplit=1)[0]
        # 解析配置文件路径以及配置文件数据库key
        db_path_name = db_name.split(":", maxsplit=1)[1]
        # 如果数据库类型是mysql
        if db_type == "mysql":
            db_connect = Database(db_path_name)

            if setup_sql[0:6].upper() == "SELECT":
                result = db_connect.fetch_all(setup_sql, ())
                INFO.logger.info("执行前置sql====>{}，获得以下结果集:{}".format(setup_sql, result))
                # 获取所有查询字段，并保存至公共参数池
                self.handler_mysql_result(result)
            elif setup_sql[0:6].upper() != "SELECT":
                rows = db_connect.execute(setup_sql, ())


        elif db_type == "redis":
            r = RedisClient(db_path_name)
            func_name = setup_sql.split(":", maxsplit=1)[0]
            key = setup_sql.split(":", maxsplit=1)[1]
            func = getattr(r, func_name)
            func(key)
        elif db_type == "sqlserver":
            # sqlserver类型封装 后续使用在来封装
            db_connect = MSSQL(db_path_name)
            if setup_sql.lower().startswith("select"):
                # result_list = db_connect.ExecQuery_dict(setup_sql, ())
                # zq切换数据源包为pyodbc后修改查询转字典
                result_list = db_connect.ExecQuery_dict(setup_sql)
                INFO.logger.info("执行前置sql====>{}，获得以下结果集:{}".format(setup_sql, result_list))
                # # 获取所有查询字段，并保存至公共参数池
                # 查询到多条数据
                for i in range(len(result_list)):
                    for key in result_list[i].keys():
                        if i != 0:
                            new_key = key + str(i)
                        else:
                            new_key = key
                        CacheHandler.update_cache(cache_name=new_key, value=result_list[i][key])
                        INFO.logger.info("保存 {}=>{} 到全局变量池".format(new_key, result_list[i][key]))
            else:
                db_connect.ExecNonQuery(setup_sql)
        # for sql in setup_sql:
        #     result = db_connect.fetch_all(sql, ())
        #     if sql.lower().startswith("select"):
        #         INFO.logger.info("执行前置sql====>{}，获得以下结果集:{}".format(sql, result))
        #         # 获取所有查询字段，并保存至公共参数池
        #         for key in result.keys():
        #             CacheHandler.update_cache(cache_name=key, value=result[key])
        #             INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, result[key]))
        elif db_type == "mongodb":
            yaml_path, test_mongodb, dbname, coll_name = db_path_name.split(":")
            yaml_path = ensure_path_sep(f"\\conf\\{yaml_path}")
            uri = GetYamlData(yaml_path).get_yaml_data()[test_mongodb]
            mongodb = MongoDBLink(uri, dbname, coll_name)
            operating_mode, param = setup_sql.split(":", maxsplit=1)
            if operating_mode == "find":
                filter_sql, projection, sort = list(map(lambda x: ast.literal_eval(x), param.split("#")))
                result_list = mongodb.filter_projection_sort_limit(filter_sql, projection, sort)
                for result in result_list:
                    for key in result.keys():
                        CacheHandler.update_cache(cache_name=key, value=result[key])
                        INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, result[key]))
            elif operating_mode == "insert":
                pass


    def handler_mysql_result(self, result_list: list):
        # 获取所有查询字段，并保存至公共参数池
        if not result_list:
            return
        if len(result_list) == 1:
            result = result_list[0]
            for key in result.keys():
                CacheHandler.update_cache(cache_name=key, value=result[key])
                INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, result[key]))
        else:
            key_list = result_list[0].keys()
            for key in key_list:
                value_list = list(map(lambda x: x.get(key), result_list))
                CacheHandler.update_cache(cache_name=key, value=value_list)
                INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, value_list))

    def execute_teardown_sql(self, db_name: str, teardown_sql):
        """
        :param db_name: mysql:shopeev2.yaml:test_jdw_database   通过冒号将数据库实例类型  conf下配置文件名称 配置文件中的第一层key
                        拼接起来，完成多数据源的直接读取  目前数据源暂且支持mysql sqlserver2种
        :param teardown_sql: sql列表 []
        :return:
        """
        # 解析数据库类型
        # mysql:walmart.yaml:test_jdw_database:test_zhcxkj_center_product
        db_type = db_name.split(":", maxsplit=1)[0]
        # 解析配置文件路径以及配置文件数据库key
        db_path_name = db_name.split(":", maxsplit=1)[1]
        # 如果数据库类型是mysql
        if db_type == "mysql":
            db_connect = Database(db_path_name)

            if teardown_sql[0:6].upper() == "SELECT":
                result = db_connect.fetch_all(setup_sql, ())
                INFO.logger.info("执行前置sql====>{}，获得以下结果集:{}".format(setup_sql, result))
                # 获取所有查询字段，并保存至公共参数池
                self.handler_mysql_result(result)
            elif setup_sql[0:6].upper() != "SELECT":
                rows = db_connect.execute(setup_sql, ())


        elif db_type == "redis":
            r = RedisClient(db_path_name)
            func_name = setup_sql.split(":", maxsplit=1)[0]
            key = setup_sql.split(":", maxsplit=1)[1]
            func = getattr(r, func_name)
            func(key)
        elif db_type == "sqlserver":
            # sqlserver类型封装 后续使用在来封装
            db_connect = MSSQL(db_path_name)
            if setup_sql.lower().startswith("select"):
                # result_list = db_connect.ExecQuery_dict(setup_sql, ())
                # zq切换数据源包为pyodbc后修改查询转字典
                result_list = db_connect.ExecQuery_dict(setup_sql)
                INFO.logger.info("执行前置sql====>{}，获得以下结果集:{}".format(setup_sql, result_list))
                # # 获取所有查询字段，并保存至公共参数池
                # 查询到多条数据
                for i in range(len(result_list)):
                    for key in result_list[i].keys():
                        if i != 0:
                            new_key = key + str(i)
                        else:
                            new_key = key
                        CacheHandler.update_cache(cache_name=new_key, value=result_list[i][key])
                        INFO.logger.info("保存 {}=>{} 到全局变量池".format(new_key, result_list[i][key]))
            else:
                db_connect.ExecNonQuery(setup_sql)
        # for sql in setup_sql:
        #     result = db_connect.fetch_all(sql, ())
        #     if sql.lower().startswith("select"):
        #         INFO.logger.info("执行前置sql====>{}，获得以下结果集:{}".format(sql, result))
        #         # 获取所有查询字段，并保存至公共参数池
        #         for key in result.keys():
        #             CacheHandler.update_cache(cache_name=key, value=result[key])
        #             INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, result[key]))
        elif db_type == "mongodb":
            yaml_path, test_mongodb, dbname, coll_name = db_path_name.split(":")
            yaml_path = ensure_path_sep(f"\\conf\\{yaml_path}")
            uri = GetYamlData(yaml_path).get_yaml_data()[test_mongodb]
            mongodb = MongoDBLink(uri, dbname, coll_name)
            operating_mode, param = setup_sql.split(":", maxsplit=1)
            if operating_mode == "find":
                filter_sql, projection, sort = list(map(lambda x: ast.literal_eval(x), param.split("#")))
                result_list = mongodb.filter_projection_sort_limit(filter_sql, projection, sort)
                for result in result_list:
                    for key in result.keys():
                        CacheHandler.update_cache(cache_name=key, value=result[key])
                        INFO.logger.info("保存 {}=>{} 到全局变量池".format(key, result[key]))
            elif operating_mode == "insert":
                pass

    def handler_assert_re(self, assert_data, res):
        value_types = ['int:', 'bool:', 'list:', 'dict:', 'tuple:', 'float:']
        actual_value = assert_data["path"]
        expect_value = assert_data['value']
        if any(i in actual_value for i in value_types) is True:
            func_name = actual_value.split(':')[0]
            erp = actual_value.split(':')[1]
            actual_value = re.findall(erp, res.text)
            if not actual_value:
                ERROR.logger.error("该jsonpath未匹配到值,请确认接口响应和path正确性")
            actual_value = actual_value[0]
            actual_value = func_name + "(" + actual_value + ")"
            actual_value = eval(actual_value)
        else:
            actual_value = re.findall(actual_value, res.text)
            if not actual_value:
                ERROR.logger.error("该jsonpath未匹配到值,请确认接口响应和path正确性")
            actual_value = actual_value[0]
        if isinstance(expect_value, str):
            if any(i in expect_value for i in value_types) is True:
                func_name = expect_value.split(':')[0]
                erp = expect_value.split(':')[1]
                expect_value = func_name + "(" + expect_value + ")"
                expect_value = eval(expect_value)
        load_module_functions(assert_type)[assert_data.get('type')](actual_value, expect_value)
        # assert_equals(actual_value, expect_value, assert_data.get('type'))

    def handler_assert_json(self, assert_data, res):
        value_types = ['int:', 'bool:', 'list:', 'dict:', 'tuple:', 'float:']
        actual_value = assert_data["path"]
        expect_value = assert_data['value']
        if any(i in actual_value for i in value_types) is True:
            func_name = actual_value.split(':')[0]
            erp = actual_value.split(':')[1]
            actual_value = jsonpath.jsonpath(res.json(), erp)
            if not actual_value:
                ERROR.logger.error("该jsonpath未匹配到值,请确认接口响应和path正确性")
            actual_value = actual_value[0]
            actual_value = func_name + "(" + actual_value + ")"
            actual_value = eval(actual_value)
        else:
            actual_value = jsonpath.jsonpath(res.json(), actual_value)
            if not actual_value:
                ERROR.logger.error("该jsonpath未匹配到值,请确认接口响应和path正确性")
            if len(actual_value) == 1:
                actual_value = actual_value[0]
        if isinstance(expect_value, str):
            if any(i in expect_value for i in value_types) is True:
                func_name = expect_value.split(':')[0]
                erp = expect_value.split(':')[1]
                expect_value = func_name + "(" + expect_value + ")"
                expect_value = eval(expect_value)
        load_module_functions(assert_type)[assert_data.get('type')](actual_value, expect_value)
        # assert_equals(actual_value, expect_value, assert_data.get('type'))

    def handler_assert_data(self, assert_data):
        expect_value = assert_data.get('path', None)

        actual_value = assert_data.get('value', None)
        load_module_functions(assert_type)[assert_data.get('type')](actual_value, expect_value)

    def handler_assert(self, assert_context, res):
        if assert_context:
            for i in assert_context:
                i = build_param(i)
                if i["AssertType"] == None:
                    if i["expressionType"] == "json":
                        self.handler_assert_json(i, res)
                    elif i["expressionType"] == "re":
                        self.handler_assert_re(i, res)



                elif i["AssertType"] == "SQL":

                    if i["expressionType"] == "json":
                        self.handler_assert_json(i, res)
                    elif i["expressionType"] == "re":
                        self.handler_assert_re(i, res)
                    elif i.get("expressionType") == "CACHE":
                        self.handler_assert_data(i)
                    elif not i.get("expressionType"):
                        self.hander_assert_no_expressionType(i)

    def hander_assert_no_expressionType(self, assert_data):
        load_module_functions(assert_type)[assert_data.get('type')](assert_data.get('value'), assert_data.get('except'))
        # self.assert_equals(i.get('value'), i.get('except'), i.get('type'))


if __name__ == '__main__':
    db_name = "redis:walmart.yaml:test_jdw_redis:8"
    setup_sql = "delete:walmart:feed:sku:11623"
    BaseTool().execute_setup_sql(db_name, setup_sql)
